package com.dage.salesflow.validation;

import com.alibaba.excel.util.MapUtils;
import com.dage.salesflow.App;
import com.dage.salesflow.constant.SysConstant;
import com.dage.salesflow.constant.YesNoEnum;
import com.dage.salesflow.kit.ObjectKit;
import com.dage.salesflow.kit.PathClassScanner;
import com.dage.salesflow.kit.StringKit;
import com.jfinal.kit.StrKit;
import io.jboot.db.annotation.Table;
import io.jboot.exception.JbootException;

import java.lang.reflect.Field;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

/**
 * 获取验证信息
 */
public class ValidationKit {

	private static final Map<Class, ValidationInfo> validationMap = MapUtils.newHashMap();
	private static final Map<Class, Set<DelForeignInfo>> delMap = MapUtils.newHashMap();

	static {
		//扫描model包下所有实体类，构建验证信息和外键关联信息
		init(App.class.getPackage().getName() + ".model");
	}

	public static void init(String modelPackage) {
		Map<String, Class> recordMapping = MapUtils.newHashMap();
		new PathClassScanner(modelPackage, recordClass -> {
			if (recordClass.isAnnotationPresent(Table.class)) {
				Table table = (Table) recordClass.getAnnotation(Table.class);
				recordMapping.put(table.tableName(), recordClass);
				if (recordClass.isAnnotationPresent(ExcelModel.class)) {
					ExcelModel excelModel = (ExcelModel) recordClass.getAnnotation(ExcelModel.class);
					try {
						ValidationInfo validationInfo = new ValidationInfo();
						Class excelClass = excelModel.excelClass();
						//导入唯一键
						if (excelClass.isAnnotationPresent(ImportKey.class)) {
							ImportKey importKey = (ImportKey) excelClass.getAnnotation(ImportKey.class);
							validationInfo.importKey.logicDelete = importKey.logicDelete();
							validationInfo.importKey.withDataScope = importKey.withDataScope();
							validationInfo.importKey.msg = importKey.msg();
							for (String column : importKey.columns()) {
								ValidationInfo.FieldInfo fieldInfo = new ValidationInfo.FieldInfo();
								fieldInfo.column = column;
								fieldInfo.recordGetter = recordClass.getMethod("get" + StrKit.firstCharToUpperCase(StrKit.toCamelCase(column)));
								validationInfo.importKey.fieldInfos.add(fieldInfo);
							}
						}
						Map<String, Field> fieldMap = ObjectKit.getClassFieldMap(excelClass);
						validationInfo.table = table.tableName();
						validationInfo.name = excelModel.name();
						for (Field field : fieldMap.values()) {
							//唯一性验证
							if (field.isAnnotationPresent(UniqueKey.class)) {
								UniqueKey uniqueKey = field.getAnnotation(UniqueKey.class);
								ValidationInfo.UniqueKeyInfo uki = new ValidationInfo.UniqueKeyInfo();
								if (StrKit.isBlank(uniqueKey.column())) {
									uki.fieldInfo.column = field.getName();
								} else {
									uki.fieldInfo.column = uniqueKey.column();
								}
								uki.msg = uniqueKey.value();
								uki.withDataScope = uniqueKey.withDataScope();
								uki.logicDelete = uniqueKey.logicDelete();
								uki.fieldInfo.recordGetter = recordClass.getMethod("get" + StrKit.firstCharToUpperCase(field.getName()));
								//							uki.recordGetter= ReflectUtil.searchMethod(record, m-> m.getName().equals("get"+StrKit.firstCharToUpperCase(field.getName())));
								uki.fieldInfo.excelField = field;
								validationInfo.uniqueKeyMap.put(field, uki);
							}
							//非空验证
							if (field.isAnnotationPresent(NotNull.class)) {
								NotNull notNull = field.getAnnotation(NotNull.class);
								ValidationInfo.NotNullInfo nni = new ValidationInfo.NotNullInfo();
								nni.msg = notNull.value();
								nni.defaultValue = notNull.defaultValue();
								validationInfo.notNullMap.put(field, nni);
							}
							//外键关联
							if (field.isAnnotationPresent(ForeignKey.class)) {
								ForeignKey foreignKey = field.getAnnotation(ForeignKey.class);
								ValidationInfo.ForeignKeyInfo fki = new ValidationInfo.ForeignKeyInfo();
								fki.msg = foreignKey.msg();
								fki.fieldInfo.column = foreignKey.column();
								fki.table = foreignKey.table();
								fki.sql = foreignKey.sql();
								fki.withDataScope = foreignKey.withDataScope();
								fki.logicDelete = foreignKey.logicDelete();
								fki.notNull = foreignKey.notNull();
								fki.fieldInfo.excelField = fieldMap.get(field.getName() + "Str");
								fki.fieldInfo.recordSetter = recordClass.getMethod("set" + StrKit.firstCharToUpperCase(field.getName()), field.getType());
								validationInfo.foreignKeyMap.put(field, fki);
							}
							//字典字段
							if (field.isAnnotationPresent(DictKey.class)) {
								DictKey dictKey = field.getAnnotation(DictKey.class);
								ValidationInfo.DictKeyInfo dki = new ValidationInfo.DictKeyInfo();
								dki.msg = dictKey.msg();
								dki.dictKey = dictKey.dictKey();
								dki.notNull = dictKey.notNull();
								dki.fieldInfo.excelField = fieldMap.get(field.getName() + "Str");
								dki.fieldInfo.recordSetter = recordClass.getMethod("set" + StrKit.firstCharToUpperCase(field.getName()), field.getType());
								validationInfo.dictKeyMap.put(field, dki);
							}
							//正则验证
							if (field.isAnnotationPresent(RegexValidation.class)) {
								RegexValidation regexValidation = field.getAnnotation(RegexValidation.class);
								ValidationInfo.RegexValidationInfo rvi = new ValidationInfo.RegexValidationInfo();
								rvi.msg = regexValidation.msg();
								rvi.pattern = regexValidation.pattern();
								rvi.recordSetter = recordClass.getMethod("set" + StrKit.firstCharToUpperCase(field.getName()), field.getType());
								validationInfo.regExMap.put(field, rvi);
							}
							//枚举属性
							if (field.isAnnotationPresent(EnumProp.class)) {
								EnumProp enumProp = field.getAnnotation(EnumProp.class);
								ValidationInfo.EnumPropInfo epi = new ValidationInfo.EnumPropInfo();
								epi.msg = enumProp.msg();
								epi.yesNo = enumProp.yesNo();
								if (epi.yesNo) {
									epi.props = SysConstant.YES_NO_PROP;
								} else {
									epi.props = enumProp.props();
								}
								epi.fieldInfo.recordSetter = recordClass.getMethod("set" + StrKit.firstCharToUpperCase(field.getName()), field.getType());
								epi.fieldInfo.excelField = fieldMap.get(field.getName() + "Name");
								validationInfo.enumPropMap.put(field, epi);
							}
						}
						validationMap.put(excelClass, validationInfo);
					} catch (NoSuchMethodException e) {
						throw new JbootException(e);
					}
				}
			}
		}).scan();


		//构建删除校验信息
		for (ValidationInfo validationInfo : validationMap.values()) {
			for (Map.Entry<Field, ValidationInfo.ForeignKeyInfo> entry : validationInfo.foreignKeyMap.entrySet()) {
				Class recordClass = recordMapping.get(entry.getValue().table);
				Set<DelForeignInfo> delSqls = delMap.get(recordClass);
				if (delSqls == null) {
					delSqls = new HashSet<>();
					delMap.put(recordClass, delSqls);
				}
				DelForeignInfo delForeignInfo = new DelForeignInfo();
				delForeignInfo.sql = "SELECT COUNT(*) FROM `" + validationInfo.table + "` WHERE " + StringKit.camelCase2Underline(entry.getKey().getName()) + "=?";
				if (entry.getValue().logicDelete) {
					delForeignInfo.sql += " AND is_deleted=" + YesNoEnum.NO.getValue();
				}
				delForeignInfo.name = validationInfo.name;
				delSqls.add(delForeignInfo);
			}
			for (Map.Entry<Field, ValidationInfo.DictKeyInfo> entry : validationInfo.dictKeyMap.entrySet()) {
				Class recordClass = recordMapping.get(SysConstant.DICT_TABLE);
				Set<DelForeignInfo> delSqls = delMap.get(recordClass);
				if (delSqls == null) {
					delSqls = new HashSet<>();
					delMap.put(recordClass, delSqls);
				}
				DelForeignInfo delForeignInfo = new DelForeignInfo();
				delForeignInfo.sql = "SELECT COUNT(*) FROM `" + validationInfo.table + "` WHERE " + StringKit.camelCase2Underline(entry.getKey().getName()) + "=?";
				delForeignInfo.name = validationInfo.name;
				delSqls.add(delForeignInfo);
			}
		}
	}

	/**
	 * 获取指定类的验证信息
	 *
	 * @param excel ExcelModel类
	 */
	public static ValidationInfo getValidationInfo(Class excel) {
		return validationMap.get(excel);
	}

	/**
	 * 获取指定数据实体类的删除校验信息
	 */
	public static Set<DelForeignInfo> getDelForeignInfo(Class record) {
		return delMap.get(record);
	}
}
